#include <stdlib.h>
#include <stdio.h>
#include "SymTable.h"
#include "SymEntry.h"
#include "StoreSymEntry.h"
#include "SymInfo.h"
#include "Key.h"

SymTable::SymTable(const size_t size) :
		storeSize(1024 + 4), symEntryStore(storeSize), symInfoStore(storeSize), overallLength(
				1026) {
	table = new SymEntry*[size];
	memset(table, 0, size * sizeof(SymEntry*));

	this->size = size;
	used = 0;

	lastBlock = table;
	lastBlockSize = 0;
}

SymTable::~SymTable() {
	SymEntry** currentBlock = table;

	puts("symtable delete1");

	for (size_t i = 0; i < size; ++i) {
		delete[] currentBlock[i];
	}

	puts("symtable delete2");
	delete[] table;
	puts("symtable delete3");
}

void SymTable::expand() {
	lastBlockSize *= 2;
	SymEntry** newBlock = new SymEntry*[lastBlockSize];
	memset(newBlock, 0, lastBlockSize * sizeof(SymEntry*));

	writeNextBlockPosition(newBlock); // write pointer to new block

	lastBlock = newBlock;
	// skip over the pointer we just wrote
	*((SymEntry**) lastBlock) = 0;
	sizeFree = lastBlockSize - sizeof(SymEntry*);
	overallLength += lastBlockSize; // lastBlocksize is already doubled
}

void SymTable::checkIfExpandNeeded() {
	// magic number will be optimized later
	/* if (used / overallLength <= 0.7) {
	 fprintf(stderr, "need expand\n");
	 expand();
	 }*/
}

void SymTable::writeNextBlockPosition(SymEntry** next) {
	*(lastBlock) = (SymEntry*) next;
}

#define END_OF_LIST 0
Key SymTable::insert(char* lexem, TokenType type) {
	// generate hash
	int hashedKey = hashme(lexem) % size + sizeof(int*);
	// save for performance reasons;
	SymEntry* SymEntryIterator;

	if (table[hashedKey]) {
		SymEntryIterator = table[hashedKey];;
		do {
			if (SymEntryIterator->matchesLexem(lexem)) {
				return Key(SymEntryIterator->info); // SymEntry found
			} else {
				SymEntryIterator = SymEntryIterator->next;
			}
		} while(SymEntryIterator != END_OF_LIST);

		SymInfo* newSymInfo = symInfoStore.add(lexem, type);
		SymEntry* newSymEntry = symEntryStore.add(newSymInfo, lexem);

		SymEntryIterator->next = newSymEntry;

		++used;
		checkIfExpandNeeded();
		return Key(newSymInfo);
	} else {
		// hash table row is still empty
		SymInfo* newSymInfo = symInfoStore.add(lexem, type);
		SymEntry* newSymEntry = symEntryStore.add(newSymInfo, lexem);
		table[hashedKey] = newSymEntry;

		++used;
		checkIfExpandNeeded();
		return Key(newSymInfo);
	}
}

SymInfo* SymTable::lookup(Key key) {
	// simple abstraction layer
	return key.getValue();
}

